#include <algorithm>
#include "esp_log.h"
#include "my_switch.hpp"

#define TAG "SWITCH"


MySwitch::MySwitch(gpio_num_t pin, bool level)
    : m_active_level(level)
    , m_ctrl_pin(pin)
    , m_state(SwitchState::IDLE)
    , m_time_left(0)
    , m_count_left(0)
    , m_start_second(0)
    , m_timer_handle(nullptr)
    , m_on_work_done(nullptr)
    , m_on_work_done_arg(nullptr)
{
    if(pin <= GPIO_NUM_NC || pin >= GPIO_NUM_MAX) {
        ESP_LOGE(TAG, "错误的引脚号码：%d", pin);
        pin = GPIO_NUM_NC;
        return;
    }

    gpio_config_t switch_pin = {
        .pin_bit_mask = 1ULL << m_ctrl_pin,
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    ESP_ERROR_CHECK(gpio_config(&switch_pin));
    turn_off();

    m_timer_handle = xTimerCreate("MySwitch Timer",
            pdMS_TO_TICKS(1000*10),
            pdTRUE,
            this,
            [](TimerHandle_t xTimer) {
                auto my_switch = (MySwitch*)pvTimerGetTimerID(xTimer);
                my_switch->toggle_state();
            }
        );
}

MySwitch::~MySwitch()
{
    if(m_timer_handle != nullptr) {
        xTimerDelete(m_timer_handle, 0);
    }
}

void MySwitch::turn_on()
{
    gpio_set_level(m_ctrl_pin, m_active_level ? 1 : 0);
    ESP_LOGW(TAG, "Start");
}

void MySwitch::turn_off()
{
    ESP_LOGW(TAG, "Stop");
    gpio_set_level(m_ctrl_pin, m_active_level ? 0 : 1);
}

/// @brief 开头状态切换逻辑
void MySwitch::toggle_state()
{
    if(m_state == SwitchState::RUN) {
        m_time_left--;
        if(m_time_left == 0) {
            m_count_left--;
            if(m_count_left == 0) {
                // 转为停止
                turn_off();
                m_state = SwitchState::FINISHED;
                xTimerStop(m_timer_handle, 0);
                if(m_on_work_done != nullptr) {
                    m_on_work_done(m_on_work_done_arg);
                }
            } else {
                // 转为暂停
                m_state = SwitchState::PAUSE;
                m_time_left = m_work_para.pausetime;
                turn_off();
            }
        }
    } else if(m_state == SwitchState::PAUSE) {
        m_time_left--;
        if(m_time_left == 0) {
            if(m_count_left != 0) {
                // 转入运行
                m_state = SwitchState::RUN;
                m_time_left = m_work_para.worktime;
                m_start_second = xTaskGetTickCount()*portTICK_PERIOD_MS%(60*std::max(CONFIG_MAX_WORK_TIME, CONFIG_MAX_PAUSE_TIME));
                turn_on();
            } else {
                turn_off();
                xTimerStop(m_timer_handle, 0);
                ESP_LOGE(TAG, "开关状态错误");
            }
        }
    }
}

void MySwitch::set_work_para(work_time_t work, work_time_t pause, work_count_t count)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if(m_state != SwitchState::IDLE && m_state != SwitchState::FINISHED) {
        ESP_LOGE(TAG, "运行中无法设置工作参数，请先停止。");
        return;
    }

    if(work > CONFIG_MAX_WORK_TIME) {
        ESP_LOGW(TAG, "工作时长超过最大值，修改为最大值：%d", CONFIG_MAX_WORK_TIME);
        work = CONFIG_MAX_WORK_TIME;
    } else if(work < CONFIG_MIN_WORK_TIME) {
        ESP_LOGW(TAG, "工作时长小于最小值，修改为最小值：%d", CONFIG_MIN_WORK_TIME);
        work = CONFIG_MIN_WORK_TIME;
    }

    if(pause > CONFIG_MAX_PAUSE_TIME) {
        ESP_LOGW(TAG, "暂停时长超过最大值，修改为最大值：%d", CONFIG_MAX_PAUSE_TIME);
        pause = CONFIG_MAX_PAUSE_TIME;
    } else if(pause < CONFIG_MIN_PAUSE_TIME) {
        ESP_LOGW(TAG, "暂停时长小于最小值，修改为最小值：%d", CONFIG_MIN_PAUSE_TIME);
        pause = CONFIG_MIN_PAUSE_TIME;
    }

    if(pause != 0) {
        if(count > CONFIG_MAX_WORK_COUNT) {
            ESP_LOGW(TAG, "工作次数超过最大值，修改为最大值：%d", CONFIG_MAX_WORK_COUNT);
            count = CONFIG_MAX_WORK_COUNT;
        } else if(pause < CONFIG_MIN_WORK_COUNT) {
            ESP_LOGW(TAG, "工作次数小于最小值，修改为最小值：%d", CONFIG_MIN_WORK_COUNT);
            count = CONFIG_MIN_WORK_COUNT;
        }
    } else {
        if(count != 1) {
            ESP_LOGW(TAG, "暂停时长为0，工作次数调整为：1");
            count = 1;
        }
    }
    
    m_work_para.worktime = work;
    m_work_para.pausetime = pause;
    m_work_para.workcount = count;
}

void MySwitch::set_work_done_callback(work_done_func_t callback, void* arg) 
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_on_work_done = callback;
    m_on_work_done_arg = arg;
}

void MySwitch::start()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if(m_state == SwitchState::RUN || m_state == SwitchState::PAUSE) {
        return;
    }
    if(m_work_para.worktime < CONFIG_MIN_WORK_TIME || m_work_para.worktime > CONFIG_MAX_WORK_TIME) {
        ESP_LOGE(TAG, "工作时间参数错误");
        return;
    }
    if(m_work_para.pausetime < CONFIG_MIN_PAUSE_TIME || m_work_para.pausetime > CONFIG_MAX_PAUSE_TIME) {
        ESP_LOGE(TAG, "暂停时间参数错误");
        return;
    }
    if(m_work_para.workcount < CONFIG_MIN_WORK_COUNT || m_work_para.workcount > CONFIG_MAX_WORK_COUNT) {
        ESP_LOGE(TAG, "工作次数参数错误");
        return;
    }
    
    // 启动定时器
    m_state = SwitchState::RUN;
    m_time_left = m_work_para.worktime;
    m_count_left = m_work_para.workcount;
    m_start_second = xTaskGetTickCount()*portTICK_PERIOD_MS%(60*std::max(CONFIG_MAX_WORK_TIME, CONFIG_MAX_PAUSE_TIME));
    turn_on();
    xTimerStart(m_timer_handle, 0);
}

void MySwitch::stop()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if(m_state == SwitchState::FINISHED || m_state == SwitchState::IDLE) {
        return;
    }
    
    auto minute = m_work_para.worktime * (m_work_para.workcount - m_count_left);
    if(m_state == SwitchState::RUN) {
        uint16_t second = xTaskGetTickCount()*portTICK_PERIOD_MS%(60*std::max(CONFIG_MAX_WORK_TIME, CONFIG_MAX_PAUSE_TIME));
        m_work_second = second > m_start_second ? second - m_start_second : second + (UINT16_MAX - m_start_second);
        minute += (m_work_para.worktime - m_time_left);
    }
    turn_off();
    xTimerStop(m_timer_handle, 0);
    m_state = SwitchState::FINISHED;
    m_work_info = R"({"workpara":{"work":)" + std::to_string(m_work_para.worktime) + R"(,)";
    m_work_info += R"("pause":)" + std::to_string(m_work_para.pausetime) + R"(,)";
    m_work_info += R"("count":)" + std::to_string(m_work_para.workcount) + R"(},)";
    m_work_info += R"("worktime":{"minute":)" + std::to_string(minute)+ R"(,)";
    m_work_info += R"("second":)" + std::to_string(m_work_second) + R"(}})";
}